# 兼容性修复：为 Flask 2.3+ 提供 JSONEncoder 替代方案
import json

import flask.json

# Monkey patching: 替换 flask.json.JSONEncoder 为标准库的 json.JSONEncoder
if not hasattr(flask.json, 'JSONEncoder'):
    flask.json.JSONEncoder = json.JSONEncoder

# 新增pymysql安装检查
try:
    import pymysql
except ImportError:
    raise RuntimeError("请先安装pymysql模块：pip install pymysql")

import logging
from logging.handlers import RotatingFileHandler

# 主程序入口文件，配置Flask应用和路由
from flask import Flask, render_template, request, jsonify, send_from_directory
from flask_caching import Cache
from flask_wtf.csrf import CSRFProtect
from flask_sqlalchemy import SQLAlchemy  # 新增：导入SQLAlchemy

from utils.baidu_map_api import BaiduMapAPI  # 新增：导入BaiduMapAPI类
from utils.qr_generator import QRGenerator

# 配置日志记录
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger('baidu_map_service')
handler = RotatingFileHandler('logs/baidu_map_service.log', maxBytes=1024*1024, backupCount=5)
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
handler.setFormatter(formatter)
logger.addHandler(handler)

import config
import datetime  # 新增：导入datetime模块
import os  # 新增：导入os模块
import shutil  # 新增：导入shutil模块

from services.baidu_map_service import BaiduMapService
from services.route_service import RouteService

# 修改导入路径：将models改为app.models（因为app.py在app父目录）

app = Flask(__name__)
app.config['SECRET_KEY'] = 'ac59075b964b0715'  # 安全密钥配置

# 新增：添加SQLAlchemy数据库连接URI配置
app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql+pymysql://jd:nETa4BGHjzShyAPD@172.16.250.188:3306/jd'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False  # 禁用跟踪修改以提高性能

# 新增：确保在应用上下文中初始化SQLAlchemy
with app.app_context():
    db = SQLAlchemy(app)  # 初始化SQLAlchemy实例
    db.create_all()  # 确保模型对应的表在数据库中存在

csrf = CSRFProtect(app)  # 初始化CSRF保护

try:
    import redis
except ImportError:
    raise RuntimeError("Redis module not found. Please install it using 'pip install redis'.")

# 修改缓存配置为Redis
cache = Cache(config=config.REDIS_CONFIG)  # 使用config.py中的REDIS_CONFIG配置

# 新增数据库模块导入
from data_modules.mysql_db import db


# 修改数据库初始化顺序（确保先检查依赖再初始化）

# 新增：创建数据库表结构（如果尚未存在）

# 修改Redis客户端使用新模块
@app.after_request
def apply_caching(response):
    """设置静态资源缓存策略"""
    if request.path.startswith('/static'):
        response.cache_control.max_age = 86400  
        response.cache_control.public = True
    return response

baidu_map_service = BaiduMapService(
    cache=cache
)
route_service = RouteService()
qr_generator = QRGenerator()

@app.route("/")
def index():
    """首页路由，渲染用户入口界面"""
    return render_template("index.html")

@app.route("/recommendation")
def recommendation():
    attractions = session.get('attraction_details', [])
    return render_template("recommendation.html", attractions=attractions)

@app.route("/preview")
def preview():
    return render_template("preview.html")

@app.route("/generate_route", methods=["POST"])
def generate_route():
    try:
        budget = float(request.form.get("budget"))
        interests = request.form.getlist("interest[]")
        time = float(request.form.get("time"))
        route_type = request.form.get("route_type", "riding")  

        # 数据库查询逻辑
        from data_modules.mysql_db import Place
        from services.algorithm_service import AlgorithmService
        algorithm_service = AlgorithmService()

        # 调用算法服务
        algorithm_result = algorithm_service.calculate_route(
            interests=interests,
            budget=budget,
            time=time,
            route_type=route_type
        )
        
        # 从算法结果获取景点数据
        places = algorithm_result.get('attraction_details', [])
        selected_attractions = algorithm_result.get('attraction_details', [])
        locations = algorithm_result.get('locations', [])
        logger.info(f'获取到的places值: {places}')
        
        if 'error' in algorithm_result:
            return render_template("recommendation.html", error_message=algorithm_result['error'])
        
        # 解析算法服务返回的坐标数据
        longitude_list = [coord['longitude'] for coord in locations]
        latitude_list = [coord['latitude'] for coord in locations]

        # 确保places列表不为空
        if not places:
            # 新增：记录未找到景点的日志
            logger.warning(f"未找到符合条件的景点，查询条件：interests={interests}, budget={budget}, time={time}")
            return render_template("recommendation.html", error_message="未找到符合条件的景点，请调整筛选条件。建议：1. 扩大预算范围 2. 选择更多兴趣类别 3. 延长游玩时间")

        # 计算所有景点的总预算
        total_cost = sum(p.budget for p in places)
        logger.info(f'计算得到的total_cost值: {total_cost}')

        # 如果总预算超过用户预算，尝试减少景点
        if total_cost > budget:
            # 按预算从低到高排序
            places_sorted = sorted(places, key=lambda x: x.budget)
            # 逐步减少景点，直到总预算不超过用户预算
            while total_cost > budget and len(places_sorted) > 1:
                places_sorted.pop()  # 移除预算最高的景点
                total_cost = sum(p.budget for p in places_sorted)
            places = places_sorted

        # 如果总预算仍然超过用户预算，返回错误信息
        if total_cost > budget:
            logger.warning(f"景点总预算超过用户预算，总预算：{total_cost}, 用户预算：{budget}")
            return render_template("recommendation.html", error_message="景点总预算超过您的预算，请调整筛选条件。建议：1. 扩大预算范围 2. 选择更经济的景点")

        # 构造路线参数
        locations = [f"{p.latitude},{p.longitude}" for p in places]  # 动态绑定经纬度
        if len(locations) >= 2:
            origin = locations[0]
            destination = locations[-1]
            waypoints = locations[1:-1]
        else:
            # 确保始终赋值origin和destination
            origin = locations[0] if locations else ""
            destination = origin  # 当只有一个地点时起点终点相同
            waypoints = []

        # 调用百度地图API
        if route_type == 'riding':
            # 构造百度地图API参数
            origin_coord = f"{latitude_list[0]},{longitude_list[0]}"
            dest_coord = f"{latitude_list[-1]},{longitude_list[-1]}"
            # 按照百度地图要求格式构造坐标参数（纬度在前）
            waypoints = [f"{lat},{lon}" for lat, lon in zip(latitude_list[1:-1], longitude_list[1:-1])] if len(latitude_list) > 2 else []
            
            route_url = baidu_map_service.get_riding_direction_url(
                origin=origin_coord,
                destination=dest_coord,
                waypoints=waypoints
            )
        elif route_type == 'driving':
            route_url = baidu_map_service.get_driving_direction_url(
                origin, destination, waypoints
            )
        else:
            route_url = baidu_map_service.get_walking_direction_url(
                origin, destination
            )

        # 计算总预算和路线描述
        route_description = f"路线包含{len(places)}个景点"

                # 将景点数据存入session
        session['attraction_details'] = algorithm_result.get('attraction_details', [])
        
        session['route_data'] = {
            'route_url': route_url,
            'places': places,
            'total_cost': total_cost
        }
        return redirect(url_for('recommendation'))
    except Exception as e:
        # 新增：记录异常日志
        logger.error(f"生成路线时发生错误：{str(e)}")
        return render_template("recommendation.html", error_message=f"系统错误：{str(e)}，请联系管理员")

@app.route("/city_map")
def city_map():
    """显示嵌入的百度地图页面"""
    return render_template("city_map.html")

@app.route("/map")
def map():
    return render_template("map.html")

@app.route("/console")
def console():
    return render_template("console.html")

@app.route('/log_management')
def log_management():
    return render_template('log_management.html')

@app.route('/system_metrics')
def system_metrics():
    # 新增会话计数字段
    return jsonify({
        'cpu_usage': 0,
        'memory_usage': 0,
        'current_sessions': 15  # 假设当前会话数
    })

@app.route('/system_services')
def system_services():
    # 模拟服务数据（需后端实现真实数据获取）
    return jsonify([
        {'name': 'API服务', 'memory_usage': 23},
        {'name': '数据库', 'memory_usage': 18},
        {'name': '缓存服务', 'memory_usage': 12}
    ]) # 保持现有实现，后续可扩展真实数据源

@app.route('/api_logs')
def get_api_logs():
    """获取API服务日志内容"""
    with open('logs/baidu_map_service.log', 'r') as f:
        content = f.read()
    return jsonify({
        'content': content,
        'lineCount': len(content.split('\n'))
    })

@app.route('/download_api_log')
def download_api_log():
    """下载API服务日志"""
    return send_from_directory('logs', 'baidu_map_service.log', as_attachment=True)

@app.route('/clear_api_log', methods=['POST'])
def clear_api_log():
    """清空API服务日志并保存历史记录"""
    log_path = 'logs/baidu_map_service.log'
    backup_path = f'logs/llogs/{datetime.datetime.now().strftime("%Y%m%d_%H%M%S")}.log'
    
    if not os.path.exists('logs/llogs'):
        os.makedirs('logs/llogs')
    
    shutil.copy(log_path, backup_path)
    open(log_path, 'w').close()
    
    return jsonify({'message': '日志已清空并备份'})

@app.route('/generate_qrcode', methods=['POST'])
def generate_qrcode():
    """生成二维码并返回Base64编码"""
    try:
        data = request.json
        url = data.get('url')

        if not url:
            return jsonify({'error': '缺少URL参数'}), 400

        # 使用QRGenerator生成二维码
        qr_generator = QRGenerator()
        qr_code_base64 = qr_generator.generate(url)

        return jsonify({'qr_code_base64': qr_code_base64})
    except Exception as e:
        return jsonify({'error': str(e)}), 500

@app.route('/check_api_key')
def check_api_key():
    """校验当前API密钥是否有效"""
    baidu_map_api = BaiduMapAPI('47jXQbiwPNWVY5GgVPUJYpOiHG5YhBcr')  # 使用当前配置的API密钥
    if baidu_map_api.validate_api_key():
        return jsonify({"status": "success", "message": "API密钥有效"})
    else:
        return jsonify({"status": "error", "message": "API密钥无效或服务被禁用"})

# 新增数据库初始化路由（用于测试连接）
@app.route('/db/test')
def test_db():
    try:
        # 验证表结构存在
        db.engine.execute('SELECT 1 FROM table1 LIMIT 1').scalar()  # 使用实际表名验证
        return jsonify({"status": "success"})
    except Exception as e:
        return jsonify({"status": "error", "message": str(e)})

@app.route('/get_feature')
def get_feature():
    attraction_id = request.args.get('id')
    feature = db.session.execute(
        'SELECT tssm FROM table1 WHERE id = :id',
        {'id': attraction_id}
    ).fetchone()
    return jsonify({'tssm': feature[0] if feature else ''})

# 在文件底部添加应用启动入口
if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000, debug=True)